#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <string>
#include <map>

#include "PropSetSimple.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

typedef std::map<std::string, std::string> mapss;

PropSetSimple::PropSetSimple() {
  mapss *props = new mapss;
  impl = static_cast<void *>( props );
}

PropSetSimple::~PropSetSimple() {
  mapss *props = static_cast<mapss *>( impl );
  delete props;
  impl = 0;
}

void PropSetSimple::Set( const char *key, const char *val, int lenKey, int lenVal ) {
  mapss *props = static_cast<mapss *>( impl );
  if( !*key ) {
    return;
  }
  if( lenKey == -1 ) {
    lenKey = static_cast<int>( strlen( key ) );
  }
  if( lenVal == -1 ) {
    lenVal = static_cast<int>( strlen( val ) );
  }
  ( *props )[std::string( key, lenKey )] = std::string( val, lenVal );
}

static bool IsASpaceCharacter( unsigned int ch ) {
  return ( ch == ' ' ) || ( ( ch >= 0x09 ) && ( ch <= 0x0d ) );
}

void PropSetSimple::Set( const char *keyVal ) {
  while( IsASpaceCharacter( *keyVal ) ) {
    keyVal++;
  }
  const char *endVal = keyVal;
  while( *endVal && ( *endVal != '\n' ) ) {
    endVal++;
  }
  const char *eqAt = strchr( keyVal, '=' );
  if( eqAt ) {
    Set( keyVal, eqAt + 1, static_cast<int>( eqAt - keyVal ),
         static_cast<int>( endVal - eqAt - 1 ) );
  } else if( *keyVal ) {
    Set( keyVal, "1", static_cast<int>( endVal - keyVal ), 1 );
  }
}

void PropSetSimple::SetMultiple( const char *s ) {
  const char *eol = strchr( s, '\n' );
  while( eol ) {
    Set( s );
    s = eol + 1;
    eol = strchr( s, '\n' );
  }
  Set( s );
}

const char *PropSetSimple::Get( const char *key ) const {
  mapss *props = static_cast<mapss *>( impl );
  mapss::const_iterator keyPos = props->find( std::string( key ) );
  if( keyPos != props->end() ) {
    return keyPos->second.c_str();
  } else {
    return "";
  }
}






struct VarChain {
  VarChain( const char *var_ = NULL, const VarChain *link_ = NULL ): var( var_ ), link( link_ ) {}

  bool contains( const char *testVar ) const {
    return ( var && ( 0 == strcmp( var, testVar ) ) )
           || ( link && link->contains( testVar ) );
  }

  const char *var;
  const VarChain *link;
};

static int ExpandAllInPlace( const PropSetSimple &props, std::string &withVars, int maxExpands, const VarChain &blankVars ) {
  size_t varStart = withVars.find( "$(" );
  while( ( varStart != std::string::npos ) && ( maxExpands > 0 ) ) {
    size_t varEnd = withVars.find( ")", varStart + 2 );
    if( varEnd == std::string::npos ) {
      break;
    }
    size_t innerVarStart = withVars.find( "$(", varStart + 2 );
    while( ( innerVarStart != std::string::npos ) && ( innerVarStart > varStart ) && ( innerVarStart < varEnd ) ) {
      varStart = innerVarStart;
      innerVarStart = withVars.find( "$(", varStart + 2 );
    }
    std::string var( withVars.c_str(), varStart + 2, varEnd - varStart - 2 );
    std::string val = props.Get( var.c_str() );
    if( blankVars.contains( var.c_str() ) ) {
      val = "";
    }
    if( --maxExpands >= 0 ) {
      maxExpands = ExpandAllInPlace( props, val, maxExpands, VarChain( var.c_str(), &blankVars ) );
    }
    withVars.erase( varStart, varEnd - varStart + 1 );
    withVars.insert( varStart, val.c_str(), val.length() );
    varStart = withVars.find( "$(" );
  }
  return maxExpands;
}

int PropSetSimple::GetExpanded( const char *key, char *result ) const {
  std::string val = Get( key );
  ExpandAllInPlace( *this, val, 100, VarChain( key ) );
  const int n = static_cast<int>( val.size() );
  if( result ) {
    memcpy( result, val.c_str(), n + 1 );
  }
  return n;
}

int PropSetSimple::GetInt( const char *key, int defaultValue ) const {
  std::string val = Get( key );
  ExpandAllInPlace( *this, val, 100, VarChain( key ) );
  if( !val.empty() ) {
    return atoi( val.c_str() );
  }
  return defaultValue;
}
